// Class info from spine-unity.dll
// 
using System.Collections.Generic;
using UnityEngine;

namespace SharpJson
{
    public class JsonDecoder
    {
        public string errorMessage
        {
            get;
            private set;
        }

        public bool parseNumbersAsFloat
        {
            get;
            set;
        }

        Lexer lexer;

        public JsonDecoder()
        {
            errorMessage = null;
            parseNumbersAsFloat = false;
        }

        public object Decode(string text)
        {
            errorMessage = null;

            lexer = new Lexer(text);
            lexer.parseNumbersAsFloat = parseNumbersAsFloat;

            return ParseValue();
        }

        public static object DecodeText(string text)
        {
            var builder = new JsonDecoder();
            return builder.Decode(text);
        }

        IDictionary<string, object> ParseObject()
        {
            var table = new Dictionary<string, object>();

            // {
            lexer.NextToken();

            while (true)
            {
                var token = lexer.LookAhead();

                switch (token)
                {
                    case Lexer.Token.None:
                        TriggerError("Invalid token");
                        return null;
                    case Lexer.Token.Comma:
                        lexer.NextToken();
                        break;
                    case Lexer.Token.CurlyClose:
                        lexer.NextToken();
                        return table;
                    default:
                        // name
                        string name = EvalLexer(lexer.ParseString());

                        if (errorMessage != null)
                            return null;

                        // :
                        token = lexer.NextToken();

                        if (token != Lexer.Token.Colon)
                        {
                            TriggerError("Invalid token; expected ':'");
                            return null;
                        }

                        // value
                        object value = ParseValue();

                        if (errorMessage != null)
                            return null;

                        table[name] = value;
                        break;
                }
            }

        }

        IList<object> ParseArray()
        {
            var array = new List<object>();

            // [
            lexer.NextToken();

            while (true)
            {
                var token = lexer.LookAhead();

                switch (token)
                {
                    case Lexer.Token.None:
                        TriggerError("Invalid token");
                        return null;
                    case Lexer.Token.Comma:
                        lexer.NextToken();
                        break;
                    case Lexer.Token.SquaredClose:
                        lexer.NextToken();
                        return array;
                    default:
                        object value = ParseValue();

                        if (errorMessage != null)
                            return null;

                        array.Add(value);
                        break;
                }
            }

        }

        object ParseValue()
        {
            switch (lexer.LookAhead())
            {
                case Lexer.Token.String:
                    return EvalLexer(lexer.ParseString());
                case Lexer.Token.Number:
                    if (parseNumbersAsFloat)
                        return EvalLexer(lexer.ParseFloatNumber());
                    else
                        return EvalLexer(lexer.ParseDoubleNumber());
                case Lexer.Token.CurlyOpen:
                    return ParseObject();
                case Lexer.Token.SquaredOpen:
                    return ParseArray();
                case Lexer.Token.True:
                    lexer.NextToken();
                    return true;
                case Lexer.Token.False:
                    lexer.NextToken();
                    return false;
                case Lexer.Token.Null:
                    lexer.NextToken();
                    return null;
                case Lexer.Token.None:
                    break;
            }

            TriggerError("Unable to parse value");
            return null;
        }

        void TriggerError(string message)
        {
            errorMessage = string.Format("Error: '{0}' at line {1}",
                                         message, lexer.lineNumber);
        }

        T EvalLexer<T>(T value)
        {
            if (lexer.hasError)
                TriggerError("Lexical error ocurred");

            return value;
        }
    }
}
